<template>
  <div class="demo">
    <h1>
      <span>绘制图形({{ storage.dnodes.length }}/{{ storage.maxSize }})</span>
      <span>&#12288;&#12288;</span>
      <div style="float: right; margin-right: 40px">
        <button @click.prevent="rebuild(false)">刷新图表</button>
        <button @click.prevent="rebuild(true)">重新生成</button>
        <button @click.prevent="storage.nodeShowable = !storage.nodeShowable">
          <span v-if="storage.nodeShowable">隐藏</span>
          <span v-else>显示</span>
          <span>节点</span>
        </button>
        <button
          @click.prevent="
            (storage.opts.compact = !storage.opts.compact), rebuild()
          "
        >
          <span v-if="storage.opts.compact">松散</span>
          <span v-else>紧凑</span>
          <span>图形</span>
        </button>
      </div>

      <div class="form">
        <div>
          <label>节点模拟总数</label>
          <input
            type="text"
            v-model="storage.maxSize"
            placeholder="最大模拟节点数"
          />
        </div>
        <div>
          <label>盒子大小W/H</label>
          <input
            type="text"
            v-model="storage.opts.size[0]"
            placeholder="盒子宽度W"
            title="盒子宽度W"
          />

          <input
            type="text"
            v-model="storage.opts.size[1]"
            placeholder="盒子高度H"
            title="盒子高度H"
          />
        </div>
        <div>
          <label>盒子间距X/Y</label>
          <input
            type="text"
            v-model="storage.opts.space[0]"
            placeholder="盒子间距X"
            title="盒子间距X"
          />
          <input
            type="text"
            v-model="storage.opts.space[1]"
            placeholder="盒子间距Y"
            title="盒子间距Y"
          />
        </div>
        <div>
          <label>图形偏移X/Y</label>
          <input
            type="text"
            v-model="storage.opts.offset[0]"
            placeholder="图形偏移X"
            title="图形偏移X"
          />
          <input
            type="text"
            v-model="storage.opts.offset[1]"
            placeholder="图形偏移Y"
            title="图形偏移Y"
          />
        </div>
        <select
          v-model="storage.opts.direction"
          title="图形方向"
          @change="onChangeGraphDirection()"
        >
          <option :value="0">从左到右</option>
          <option :value="1">从上到下</option>
        </select>
      </div>
    </h1>
    <div class="graph">
      <div
        class="scale"
        :style="`width:${storage.dsize[0]}px;height: ${storage.dsize[1]}px;`"
      >
        <svg
          :width="storage.dsize[0]"
          :height="storage.dsize[1]"
          xmlns:xlink="http://www.w3.org/1999/xlink"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            v-for="(line, i) of storage.dlines"
            :key="i"
            :d="line"
            style="
              fill: none;
              stroke: rgba(114, 114, 173, 0.473);
              stroke-width: 1;
            "
          />
        </svg>

        <div
          v-for="(node, i) in storage.dnodes"
          v-show="storage.nodeShowable"
          :key="i"
          :style="`
          left:${node.position[0]}px; top:${node.position[1]}px; width: ${node.size[0]}px; height: ${node.size[1]}px;
          height: ${node.size[1]}px;line-height: ${node.size[1]}px;
          `"
          class="node"
          :title="`p:${node.position}\ns:${node.size}`"
        >
          {{ node.name }}
          <div class="top">
            <div @click="addNode(node.name, 'top')">+</div>
            <div @click="removeNode(node.name, 'top')">-</div>
          </div>
          <div class="right">
            <div @click="addNode(node.name, 'right')">+</div>
            <div @click="removeNode(node.name, 'right')">-</div>
          </div>
          <div class="left">
            <div @click="addNode(node.name, 'left')">+</div>
            <div @click="removeNode(node.name, 'left')">-</div>
          </div>
          <div class="bottom">
            <div @click="addNode(node.name, 'bottom')">+</div>
            <div @click="removeNode(node.name, 'bottom')">-</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { reactive, ref, toRaw } from "vue";
import { mockNodes, toCalcGraphData, toSimplifyNodes, NS } from "../../src";

const storage = reactive<Record<string, any>>({
  maxSize: 35,
  nodeShowable: true,
  nodeWeight: 4,
  dsize: [0, 0],
  dlines: [],
  dnodes: [],
  opts: {
    offset: [100, 100],
    size: [45, 45],
    space: [30, 10],
    debug: false,
    compact: true,
    direction: 0,
  },
});

function onChangeGraphDirection() {
  let [x, y] = storage.opts.offset;
  storage.opts.offset = [y, x];

  [x, y] = storage.opts.space;
  storage.opts.space = [y, x];
  rebuild();
}

let nodesJsonStr = "";
function getCacheNodes() {
  if (!nodesJsonStr) nodesJsonStr = localStorage.getItem("mock_nodes") || "[]";
  try {
    const str = localStorage.getItem("mock_nodes");
    return JSON.parse(str || "[]");
  } catch (e) {
    console.log(e);
  }
  return [];
}

/**
 * 创建
 */
let data = ref<any>({});
function rebuild(refresh = false) {
  let nodes: any[] = [];
  const now = Date.now();
  if (!refresh) nodes = getCacheNodes();
  if (!nodes || !nodes.length) {
    nodes = mockNodes(storage.maxSize, storage.nodeWeight);
    if (nodes.length <= 500) {
      nodesJsonStr = JSON.stringify(nodes);
      localStorage.setItem("mock_nodes", nodesJsonStr);
    }

    storage.logs[storage.logs.length] = `模拟/获取作业(${nodes.length})耗时:${
      Date.now() - now
    }毫秒;`;
  }
  data.value = toCalcGraphData(nodes[0], storage.opts);
  storage.dnodes = data.value.dnodes;
  storage.dlines = data.value.dlines;
  storage.dsize = data.value.dsize;
  console.log(toRaw(data.value.root), toRaw(storage.dsize));
}
function findParent(obj: any, name: string): NS.XNode | null {
  if (!obj.children || !obj.children.length) return null;
  if (obj.children.some((e: any) => e.name === name)) {
    return obj;
  }
  let child = null;
  obj.children.forEach((e: any) => {
    const res = findParent(e, name);
    if (res) {
      child = res;
    }
  });
  return child;
}
let newNameCount = 1;
let sCount = 1,
  pCount = 1;
function addNode(name: string, position: string) {
  const parent = findParent(data.value.root, name);
  switch (position) {
    case "left":
      if (!parent) {
        data.value.root.children.push({
          type: 0,
          name: "NS" + sCount++,
          children: [
            { type: 2, name: "NN" + newNameCount++, children: [] },
            { type: 2, name: name, children: [] },
          ],
        });
      } else if (parent.type == 1) {
        //如果是并行节点，则把当前节点变成串行
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex, 1, {
          type: 0,
          name: "NS" + sCount++,
          children: [
            { type: 2, name: "NN" + newNameCount++, children: [] },
            { type: 2, name: name, children: [] },
          ],
        });
      } else {
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex, 0, {
          type: 2,
          name: "NN" + newNameCount++,
          children: [],
        });
      }
      break;
    case "right":
      if (!parent) {
        data.value.root.children.push({
          type: 0,
          name: "NS" + sCount++,
          children: [
            { type: 2, name: name, children: [] },
            { type: 2, name: "NN" + newNameCount++, children: [] },
          ],
        });
      } else if (parent.type == 1) {
        //如果是并行节点，则把当前节点变成串行
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex, 1, {
          type: 0,
          name: "NS" + sCount++,
          children: [
            { type: 2, name: name, children: [] },
            { type: 2, name: "NN" + newNameCount++, children: [] },
          ],
        });
      } else {
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex + 1, 0, {
          type: 2,
          name: "NN" + newNameCount++,
          children: [],
        });
      }
      break;
    case "top":
      if (!parent) {
        data.value.root.children.push({
          type: 0,
          name: "NS" + sCount++,
          children: [
            {
              type: 1,
              name: "NP" + pCount++,
              children: [
                { type: 2, name: "NN" + newNameCount++, children: [] },
                { type: 2, name: name, children: [] },
              ],
            },
          ],
        });
      } else if (parent.type == 0) {
        //如果是串行节点，则把当前节点变成并行
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex, 1, {
          type: 1,
          name: "NP" + pCount++,
          children: [
            { type: 2, name: "NN" + newNameCount++, children: [] },
            { type: 2, name: name, children: [] },
          ],
        });
      } else {
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex, 0, {
          type: 2,
          name: "NN" + newNameCount++,
          children: [],
        });
      }
      break;
    case "bottom":
      if (!parent) {
        data.value.root.children.push({
          type: 0,
          name: "NS" + sCount++,
          children: [
            {
              type: 1,
              name: "NP" + pCount++,
              children: [
                { type: 2, name: name, children: [] },
                { type: 2, name: "NN" + newNameCount++, children: [] },
              ],
            },
          ],
        });
      } else if (parent.type == 0) {
        //如果是串行节点，则把当前节点变成并行
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex, 1, {
          type: 1,
          name: "NP" + pCount++,
          children: [
            { type: 2, name: name, children: [] },
            { type: 2, name: "NN" + newNameCount++, children: [] },
          ],
        });
      } else {
        const childIndex = parent.children!.findIndex((e) => e.name === name);
        parent.children!.splice(childIndex + 1, 0, {
          type: 2,
          name: "NN" + newNameCount++,
        });
      }
      break;
    default:
      break;
  }
  const index = data.value.root.children.findIndex(
    (e: NS.XNode) => e.type === 2 && e.name === "NN" + newNameCount
  );
  if (index > -1) data.value.root.children.splice(index, 1);
  reCompile();
}
const key = ref(0);
function reCompile() {
  data.value = toCalcGraphData(data.value.root, storage.opts);
  storage.dnodes = data.value.dnodes;
  storage.dlines = data.value.dlines;
  storage.dsize = data.value.dsize;
}
function removeNode(name: string, position: string) {
  const parent = findParent(data.value.root, name);
  if (!parent) return;
  const index = parent.children!.findIndex((e: any) => e.name == name);
  key.value++;
  parent.children!.splice(index, 1);
  reCompile();
}
rebuild();
</script>

<style lang="less">
html,
body {
  margin: 0;
  padding: 0;
}
.demo {
  width: 100vw;
  height: 100vh;
  background-color: rgb(228, 233, 240);
  overflow: hidden;

  h1 {
    font-family: "Courier New", Courier, monospace;
    padding: 5px;
    margin: 0;
    // border-bottom: 1px dashed #ddd;
    width: 100%;
    overflow: hidden;
    background-color: rgb(14, 110, 206);
    color: #fff;
    padding: 10px 20px;
    font-size: 20px;
    user-select: none;

    .form {
      margin-top: 10px;
      > div {
        display: inline-block;
        margin-right: 10px;

        label {
          font-size: 14px;
          font-weight: normal;
        }
      }
    }

    select,
    button,
    input {
      height: 20px;
      line-height: 20px;
      border: none;
      max-width: 80px;
      margin: 0 2px;
      border-radius: 2px;
    }

    input {
      width: 60px;
      text-align: center;
      background-color: antiquewhite;
    }
  }
}
.graph {
  position: relative;
  width: 100vw;
  height: calc(100vh - 80px);
  overflow: scroll;
  // transform: scale(0.5);

  .scale {
    position: relative;
  }

  .node {
    position: absolute;
    top: 0;
    left: 0;
    width: 120px;
    line-height: 50px;
    font-size: 12px;
    text-align: center;
    border-radius: 5px;
    background-color: rgba(16, 71, 223, 0.505);
    font-size: 13px;
    vertical-align: middle;
    color: #fff;
    box-sizing: border-box;
    > div {
      display: none;
      z-index: 99;
      border-radius: 2px;
      > div {
        border: none !important;
        background-color: rgba(20, 19, 18, 0.105);
      }
    }
    &:hover {
      .top {
        position: absolute;
        width: 100%;
        height: 15px;
        font-size: 12px;
        display: flex;
        align-items: center;
        left: 0;
        top: -15px;
        background-color: #aaa;
        cursor: pointer;
        div {
          flex: 1;
          border: 1px solid #777;
          text-align: center;
          line-height: 15px;
        }
      }
      .right {
        position: absolute;
        width: 15px;
        height: 100%;
        font-size: 12px;
        display: flex;
        align-items: center;
        flex-direction: column;
        right: -15px;
        top: 0;
        background-color: #aaa;
        cursor: pointer;
        div {
          flex: 1;
          width: 15px;
          line-height: 80%;
          border: 1px solid #777;
          text-align: center;
        }
      }
      .left {
        position: absolute;
        width: 15px;
        height: 100%;
        font-size: 12px;
        display: flex;
        align-items: center;
        flex-direction: column;
        left: -15px;
        top: 0;
        background-color: #aaa;
        cursor: pointer;
        div {
          flex: 1;
          width: 15px;
          line-height: 80%;
          border: 1px solid #777;
          text-align: center;
        }
      }
      .bottom {
        position: absolute;
        width: 100%;
        height: 15px;
        font-size: 12px;
        display: flex;
        align-items: center;
        left: 0;
        bottom: -15px;
        background-color: #aaa;
        cursor: pointer;
        div {
          flex: 1;
          border: 1px solid #777;
          text-align: center;
          line-height: 15px;
        }
      }
    }
  }
  .node:hover {
    background-color: rgba(16, 71, 223, 0.905);
  }
}
</style>
